package service

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"photowall/pkg/config"
	"photowall/pkg/log"
	"sync"
	"time"
)

type accessToken struct {
	Token     string `json:"access_token"`
	ExpiresIn int    `json:"expires_in"`
}

var refreashAccessTokenSyncOnce sync.Once
var refreashAccessTokenLock sync.Mutex
var globalAccessToken accessToken

func refeashAccessToken() bool {
	requestUrl := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/token?grant_type=%s&appid=%s&secret=%s",
		"client_credential", config.GetConfig().WechatInfo.AppID, config.GetConfig().WechatInfo.AppSecret)

	response, err := http.Get(requestUrl)
	if err != nil {
		log.GetMainLogger().Error("GetAccessToken http error:%s", err)
		return false
	}
	defer response.Body.Close()

	if response.StatusCode/100 != 2 {
		log.GetMainLogger().Error(fmt.Sprintf("access_token Get request stauts is not 200 %v", response.StatusCode))
	}

	// 读取响应内容
	body, err := io.ReadAll(response.Body)
	if err != nil {
		log.GetMainLogger().Error("access_token read io failed:%s", err)
		return false
	}

	refreashAccessTokenLock.Lock()
	defer refreashAccessTokenLock.Unlock()

	err = json.Unmarshal(body, &globalAccessToken)
	if err != nil {
		log.GetMainLogger().Error(fmt.Sprintf("json Unmarshal access_token failed, %v", string(body)) + err.Error())
		return false
	}

	return true
}

func GetAccessToken() string {
	refreashAccessTokenSyncOnce.Do(func() {
		go refreshAccessTokenRegularly()
	})

	refreashAccessTokenLock.Lock()
	defer refreashAccessTokenLock.Unlock()

	return globalAccessToken.Token
}

func refreshAccessTokenRegularly() {
	failedCount := 0

	res := refeashAccessToken()
	if !res {
		failedCount += 1
	}

	done := make(chan struct{})

	for {
		select {
		// 设置在过期前十秒更新
		case <-time.After(time.Duration(func(num int) int {
			if num < 10 {
				return 10
			} else {
				return num
			}
		}(globalAccessToken.ExpiresIn)) * time.Second):
			res := refeashAccessToken()
			if !res {
				failedCount += 1
			}
			if failedCount >= 10 {
				done <- struct{}{}
			}

		case <-done:
			log.GetMainLogger().Error(fmt.Sprintf("refreash access_token failed, tried %v times", failedCount))
			return
		}
	}
}
